<?php

class App
{
    public function __construct()
    {
        $this->init();
        $this->route();
        $this->trace();
    }

    /**
     * 初始化
     */
    private function init()
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        // 显示所有错误
        error_reporting(E_ALL);
        ini_set('display_errors', '0');

        // 自定义异常
        set_error_handler('App::errorHandler');
        set_exception_handler('App::exceptionHandler');

        // require_once 'functions.php';
        // require_once APP_PATH . 'helpers.php';
    }

    /**
     * 解析路由
     */
    public function route()
    {
        // 维护模式
        if (config('app.maintenance')) {
            exit('网站处于维护模式中，请稍候访问。');
        }

        $router = $this->splitUrl();

        $controller_name = $router[0];
        $method_name     = $router[1];

        $controller_file = APP_PATH . 'controllers/' . ucfirst($controller_name) . '.php';

        if (!is_file($controller_file)) {
            $controller_name = DEFAULT_CONTROLLER;
            $controller_file = APP_PATH . '/controllers/' . DEFAULT_CONTROLLER . '.php';
            // 如果对应控制器不存在，显示404
            abort('404', '控制器不存在。');
        }

        // require $controller_file;

        define('CONTROLLER_NAME', $controller_name);

        $controller_name = "\\app\\controllers\\" . ucfirst($controller_name);
        $controller = new $controller_name;

        if (!method_exists($controller, $method_name)) {
            $method_name = DEFAULT_METHOD;
            abort('404', '方法不存在。');
        }

        define('METHOD_NAME', $method_name);
        define('SEGMENTS', $router);
        define('SERVER_ROOT', str_replace('index.php', '', $_SERVER['PHP_SELF']));

        array_shift($router);
        array_shift($router);
        define('URL_SEGMENT', implode('/', $router));

        $GLOBALS['traceSql'] = [];
        call_user_func([$controller, $method_name]);
    }

    /**
     * 分割网址
     *
     * @return array
     */
    private function splitUrl(): array
    {
        $url = isset($_GET['url'])
            ? filter_var(trim(escape($_GET['url']), '/'), FILTER_SANITIZE_URL)
            : null;

        if (!$url) {
            $url = DEFAULT_CONTROLLER . '/' . DEFAULT_METHOD;
        }

        if (PAGE_SUFFIX) {
            $parts = explode(PAGE_SUFFIX, $url, 2);
            $url = $parts[0];
        }

        $router = explode('/', $url);

        if (empty($router[0])) {
            array_shift($router);
        }

        $router[0] = isset($router[0]) ?  $router[0] : DEFAULT_METHOD;
        $router[1] = isset($router[1]) ?  $router[1] : DEFAULT_METHOD;

        $pageNumber = null;
        for ($i = 2; $i < count($router); $i++) {
            $match = [];
            if (preg_match('/^page([0-9]+)$/Ui', $router[$i], $match)) {
                $pageNumber = intval($match[1]);
                array_splice($router, $i, 1);
                break;
            }
        }

        // 页码使用定义的常量或默认值
        define("PAGE_NUMBER", $pageNumber ?? 1);

        return $router;
    }

    /**
     * 将错误当作异常抛出
     *
     * @param int    $code     错误编码
     * @param string $message  错误消息
     * @param string $file     引发错误的文件名
     * @param int    $line     引发错误所在行
     *
     * @return void
     */
    public static function errorHandler($level, $message, $file, $line): void
    {
        if (error_reporting() !== 0) {
            throw new ErrorException($message, 0, $level, $file, $line);
        }
    }

    /**
     * 异常处理
     *
     * @param Exception $exception  异常
     *
     * @return void
     */
    public static function exceptionHandler($exception)
    {
        // Code is 404 (not found) or 500 (general error)
        $code = $exception->getCode();
        if ($code != 404) {
            $code = 500;
        }
        http_response_code($code);

        if (config('app.debug')) {
            include_once 'templates/debug.php';
            exit();
        } else {
            $log = new Log();
            $log->debug($exception->getMessage() . '\n' . $exception->getFile() . '\n' . $exception->getLine());
            return $code;
        }
    }

    /**
     * 开启调试追踪
     */
    private function trace()
    {
        if (!config('app.trace')) {
            return false;
        }

        include_once CORE_PATH . 'templates/trace.php';
    }
}
